package com.onenine.chat.message;

import android.content.Context;
import android.media.MediaPlayer;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.chad.library.adapter.base.BaseMultiItemQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import com.chad.library.adapter.base.entity.MultiItemEntity;
import com.onenine.app.R;
import com.onenine.chat.commons.IMessage;
import com.onenine.chat.commons.ImageLoader;
import com.onenine.chat.commons.ViewHolder;

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;


public class MsgListAdapter<MESSAGE extends IMessage> extends BaseMultiItemQuickAdapter {

    private SparseArray<CustomMsgConfig> mCustomMsgList;
    private Context mContext;
    private MyMultiItemEntity mHolders;
    private ImageLoader mImageLoader;
    private boolean mIsSelectedMode;
    private OnMsgClickListener<MESSAGE> mMsgClickListener;
    private OnMsgLongClickListener<MESSAGE> mMsgLongClickListener;
    private OnAvatarClickListener<MESSAGE> mAvatarClickListener;
    private OnMsgStatusViewClickListener<MESSAGE> mMsgStatusViewClickListener;
    private SelectionListener mSelectionListener;
    private int mSelectedItemCount;
    private LinearLayoutManager mLayoutManager;
    private MessageListStyle mStyle;
    private MediaPlayer mMediaPlayer;
    private List<Wrapper> mItems;
    private boolean mScroll;

    public MsgListAdapter(ImageLoader imageLoader) {
        this(new MyMultiItemEntity(), imageLoader);
    }

    public MsgListAdapter(MyMultiItemEntity holders, ImageLoader imageLoader) {
        super(new ArrayList<>());
        mMediaPlayer = new MediaPlayer();
        mHolders = holders;
        mImageLoader = imageLoader;
        mItems = getData();
        addItemTypeViews();
    }

    public void setScrolling(boolean scroll) {
        this.mScroll = scroll;
    }

    public boolean getScrolling() {
        return this.mScroll;
    }

    public void pauseVoice() {
        try {
            mMediaPlayer.pause();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        }
    }

    public MediaPlayer getMediaPlayer() {
        return mMediaPlayer;
    }

    /**
     * 添加所有列表分类集合
     */
    private void addItemTypeViews() {
        addItemType(MyMultiItemEntity.TYPE_SEND_TXT, mHolders.mSendTxtLayout);
        addItemType(MyMultiItemEntity.TYPE_RECEIVE_TXT, mHolders.mReceiveTxtLayout);
        addItemType(MyMultiItemEntity.TYPE_SEND_VOICE, mHolders.mSendVoiceLayout);
        addItemType(MyMultiItemEntity.TYPE_RECEIVER_VOICE, mHolders.mReceiveVoiceLayout);
        addItemType(MyMultiItemEntity.TYPE_SEND_IMAGE, mHolders.mSendPhotoLayout);
        addItemType(MyMultiItemEntity.TYPE_RECEIVER_IMAGE, mHolders.mReceivePhotoLayout);
        addItemType(MyMultiItemEntity.TYPE_SEND_VIDEO, mHolders.mSendVideoLayout);
        addItemType(MyMultiItemEntity.TYPE_RECEIVE_VIDEO, mHolders.mReceiveVideoLayout);
        addItemType(MyMultiItemEntity.TYPE_EVENT, mHolders.mEventLayout);
        addItemType(MyMultiItemEntity.TYPE_FRIEND_APPLY, mHolders.mFriendApplyLayout);
    }

    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        super.onCreateViewHolder(parent, viewType);
        switch (viewType) {
            case MyMultiItemEntity.TYPE_SEND_TXT:
                return getHolder(parent, mHolders.mSendTxtLayout, mHolders.mSendTxtHolder, true);
            case MyMultiItemEntity.TYPE_RECEIVE_TXT:
                return getHolder(parent, mHolders.mReceiveTxtLayout, mHolders.mReceiveTxtHolder, false);
            case MyMultiItemEntity.TYPE_SEND_LOCATION:
                return getHolder(parent, mHolders.mSendLocationLayout, mHolders.mSendLocationHolder, true);
            case MyMultiItemEntity.TYPE_RECEIVER_LOCATION:
                return getHolder(parent, mHolders.mReceiveLocationLayout, mHolders.mReceiveLocationHolder, false);
            case MyMultiItemEntity.TYPE_SEND_VOICE:
                return getHolder(parent, mHolders.mSendVoiceLayout, mHolders.mSendVoiceHolder, true);
            case MyMultiItemEntity.TYPE_RECEIVER_VOICE:
                return getHolder(parent, mHolders.mReceiveVoiceLayout, mHolders.mReceiveVoiceHolder, false);
            case MyMultiItemEntity.TYPE_SEND_IMAGE:
                return getHolder(parent, mHolders.mSendPhotoLayout, mHolders.mSendPhotoHolder, true);
            case MyMultiItemEntity.TYPE_RECEIVER_IMAGE:
                return getHolder(parent, mHolders.mReceivePhotoLayout, mHolders.mReceivePhotoHolder, false);
            case MyMultiItemEntity.TYPE_SEND_VIDEO:
                return getHolder(parent, mHolders.mSendVideoLayout, mHolders.mSendVideoHolder, true);
            case MyMultiItemEntity.TYPE_RECEIVE_VIDEO:
                return getHolder(parent, mHolders.mReceiveVideoLayout, mHolders.mReceiveVideoHolder, false);
            case MyMultiItemEntity.TYPE_EVENT:
                return getHolder(parent, mHolders.mEventLayout, mHolders.mEventMsgHolder, true);
            case MyMultiItemEntity.TYPE_FRIEND_APPLY:
                return getHolder(parent, mHolders.mFriendApplyLayout, mHolders.mFriendApplyHolder, false);
            default:
                if (mCustomMsgList != null && mCustomMsgList.size() > 0) {
                    return getHolder(parent, mCustomMsgList.get(viewType).getResourceId(),
                            mCustomMsgList.get(viewType).getClazz(), mCustomMsgList.get(viewType).getIsSender());
                }
                return getHolder(parent, mHolders.mSendTxtLayout, mHolders.mSendLocationHolder, false);
        }
    }

    @Override
    protected void convert(BaseViewHolder holder, Object item) {
        Wrapper wrapper = mItems.get(holder.getAdapterPosition());
        if (wrapper.item instanceof IMessage) {
            DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
            BaseMessageViewHolder baseHolder = ((BaseMessageViewHolder) holder);
            baseHolder.mContext = this.mContext;
            baseHolder.mPosition = holder.getAdapterPosition();
            baseHolder.mDensity = dm.density;
            baseHolder.mIsSelected = wrapper.isSelected;
            baseHolder.mImageLoader = this.mImageLoader;
            baseHolder.mMsgLongClickListener = this.mMsgLongClickListener;
            baseHolder.mMsgClickListener = this.mMsgClickListener;
            baseHolder.mAvatarClickListener = this.mAvatarClickListener;
            baseHolder.mMsgStatusViewClickListener = this.mMsgStatusViewClickListener;
            baseHolder.mMediaPlayer = this.mMediaPlayer;
            baseHolder.mScroll = this.mScroll;
            baseHolder.mData = this.mItems;
        }
        ((ViewHolder) holder).onBind(wrapper.item, holder.getAdapterPosition());
    }

    /**
     * Specify custom gif_message config, include view type, layout resource id, is send outgoing(according to layout)
     * and custom view holder's {@link Class} object.
     *
     * @param viewType View type, must not set 0-12, otherwise will throw IllegalArgumentException
     * @param bean     {@link CustomMsgConfig}
     */
    public void addCustomMsgType(int viewType, CustomMsgConfig bean) {
        if (mCustomMsgList == null) {
            mCustomMsgList = new SparseArray<>();
        }
        mCustomMsgList.put(viewType, bean);
    }

    public SparseArray<CustomMsgConfig> getCustomMsgList() {
        return mCustomMsgList;
    }

    @Override
    public int getItemViewType(int position) {
        Wrapper wrapper = mItems.get(position);
        if (wrapper.item instanceof IMessage) {
            IMessage message = (IMessage) wrapper.item;
            if (message.getMessageType() == IMessage.MessageType.EVENT) {
                return MyMultiItemEntity.TYPE_EVENT;
            } else if (message.getMessageType() == IMessage.MessageType.SEND_TEXT) {
                return MyMultiItemEntity.TYPE_SEND_TXT;
            } else if (message.getMessageType() == IMessage.MessageType.RECEIVE_TEXT) {
                return MyMultiItemEntity.TYPE_RECEIVE_TXT;
            } else if (message.getMessageType() == IMessage.MessageType.SEND_IMAGE) {
                return MyMultiItemEntity.TYPE_SEND_IMAGE;
            } else if (message.getMessageType() == IMessage.MessageType.RECEIVE_IMAGE) {
                return MyMultiItemEntity.TYPE_RECEIVER_IMAGE;
            } else if (message.getMessageType() == IMessage.MessageType.SEND_VOICE) {
                return MyMultiItemEntity.TYPE_SEND_VOICE;
            } else if (message.getMessageType() == IMessage.MessageType.RECEIVE_VOICE) {
                return MyMultiItemEntity.TYPE_RECEIVER_VOICE;
            } else if (message.getMessageType() == IMessage.MessageType.SEND_VIDEO) {
                return MyMultiItemEntity.TYPE_SEND_VIDEO;
            } else if (message.getMessageType() == IMessage.MessageType.RECEIVE_VIDEO) {
                return MyMultiItemEntity.TYPE_RECEIVE_VIDEO;
            } else if (message.getMessageType() == IMessage.MessageType.SEND_LOCATION) {
                return MyMultiItemEntity.TYPE_SEND_LOCATION;
            } else if (message.getMessageType() == IMessage.MessageType.RECEIVE_LOCATION) {
                return MyMultiItemEntity.TYPE_RECEIVER_LOCATION;
            } else if (message.getMessageType() == IMessage.MessageType.FRIEND_APPLY) {
                return MyMultiItemEntity.TYPE_FRIEND_APPLY;
            }
        }
        return MyMultiItemEntity.TYPE_SEND_TXT;
    }

    private int getCustomType(IMessage message) {
        for (int i = 0; i < mCustomMsgList.size(); i++) {
            CustomMsgConfig config = mCustomMsgList.valueAt(i);
            if (message.getMessageType().ordinal() == config.getViewType()) {
                return config.getViewType();
            }
        }
        return MyMultiItemEntity.TYPE_SEND_TXT;
    }

    private <HOLDER extends ViewHolder> ViewHolder getHolder(ViewGroup parent, @LayoutRes int layout,
                                                             Class<HOLDER> holderClass, boolean isSender) {
        View v = LayoutInflater.from(parent.getContext()).inflate(layout, parent, false);
        try {
            Constructor<HOLDER> constructor = holderClass.getDeclaredConstructor(View.class, boolean.class);
            constructor.setAccessible(true);
            HOLDER holder = constructor.newInstance(v, isSender);
            if (holder instanceof DefaultMessageViewHolder) {
                ((DefaultMessageViewHolder) holder).applyStyle(mStyle);
            }
            return holder;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public int getItemCount() {
        if (mItems == null) {
            return 0;
        }
        return mItems.size();
    }

    public class Wrapper<DATA> {
        private DATA item;
        boolean isSelected;

        Wrapper(DATA item) {
            this.item = item;
        }

        public DATA getItem() {
            return this.item;
        }

        public boolean getIsSelected() {
            return this.isSelected;
        }

        public void setItem(DATA item) {
            this.item = item;
        }

        public void setSelected(boolean isSelected) {
            this.isSelected = isSelected;
        }
    }

    /**
     * Add gif_message to bottom of list
     *
     * @param message        gif_message to be add
     * @param scrollToBottom if true scroll list to bottom
     */
    public void addToStart(MESSAGE message, boolean scrollToBottom) {
        Wrapper<MESSAGE> element = new Wrapper<>(message);
        mItems.add(0, element);
        notifyItemRangeInserted(0, 1);
        if (mLayoutManager != null && scrollToBottom) {
            mLayoutManager.scrollToPosition(0);
        }
    }

    /**
     * get start item
     *
     * @return
     */
    public MESSAGE getStartItem() {
        if (!mItems.isEmpty()) {
            return (MESSAGE) mItems.get(0).item;
        }
        return null;
    }

    /**
     * Add messages to end, the messages in list are in descending order.
     * For example: messages[0].timeString > messages[1].timeString.
     * To load last page of messages from history, use this method.
     *
     * @param messages Last page of messages.
     */
    public void addToEnd(List<MESSAGE> messages, boolean scrollToBottom) {
        int oldSize = mItems.size();
        for (int i = 0; i < messages.size(); i++) {
            MESSAGE message = messages.get(i);
            mItems.add(new Wrapper<>(message));
        }
        notifyItemRangeInserted(oldSize, mItems.size() - oldSize);
        if (mLayoutManager != null && scrollToBottom) {
            mLayoutManager.scrollToPosition(0);
        }
    }

    /**
     * 设置未满一页消息排在end处
     *
     * @param pageSize
     */
    public void setStackFromEndPageSize(int pageSize) {
        if (mLayoutManager != null) {
            mLayoutManager.setStackFromEnd(mItems.size() < pageSize);
        }
    }

    /**
     * If messages in list is sorted chronologically, for example, messages[0].timeString < messages[1].timeString.
     * To load last page of messages from history, use this method.
     *
     * @param messages Last page of messages.
     */
    public void addToEndChronologically(List<MESSAGE> messages) {
        int oldSize = mItems.size();
        for (int i = messages.size() - 1; i >= 0; i--) {
            MESSAGE message = messages.get(i);
            mItems.add(new Wrapper<>(message));
        }
        notifyItemRangeInserted(oldSize, mItems.size() - oldSize);
    }

    @SuppressWarnings("unchecked")
    private int getMessagePositionByDatetime(long datetime) {
        for (int i = 0; i < mItems.size(); i++) {
            Wrapper wrapper = mItems.get(i);
            if (wrapper.item instanceof IMessage) {
                MESSAGE message = (MESSAGE) wrapper.item;
                if (message.getDatetime() == datetime) {
                    return i;
                }
            }
        }
        return -1;
    }

    @SuppressWarnings("unchecked")
    private int getMessagePositionById(int id) {
        for (int i = 0; i < mItems.size(); i++) {
            Wrapper wrapper = mItems.get(i);
            if (wrapper.item instanceof IMessage) {
                MESSAGE message = (MESSAGE) wrapper.item;
                if (message.getMsgId() == id) {
                    return i;
                }
            }
        }
        return -1;
    }

    public List<MESSAGE> getMessageList() {
        List<MESSAGE> msgList = new ArrayList<>();
        for (Wrapper wrapper : mItems) {
            if (wrapper.item instanceof IMessage) {
                msgList.add((MESSAGE) wrapper.item);
            }
        }
        return msgList;
    }

    public int findMessagePositionByFilePath(String filePath) {
        for (int i = 0; i < mItems.size(); i++) {
            MESSAGE message = (MESSAGE) mItems.get(i).item;
            if (TextUtils.equals(message.getMediaFilePath(), filePath)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Update gif_message by its id.
     *
     * @param message gif_message to be updated.
     */
    public void updateMessage(MESSAGE message) {
        updateMessage(message.getMsgId(), message);
    }

    /**
     * Updates gif_message by old identifier.
     *
     * @param oldId      gif_message id to be updated
     * @param newMessage gif_message to be updated
     */
    public void updateMessage(int oldId, MESSAGE newMessage) {
        int position = getMessagePositionById(oldId);
        if (position >= 0) {
            Wrapper<MESSAGE> element = new Wrapper<>(newMessage);
            mItems.set(position, element);
            notifyItemChanged(position);
        }
    }

    /**
     * Updates gif_message or add gif_message if oldId not exist.
     *
     * @param oldId          gif_message id to be updated
     * @param newMessage     gif_message to be updated
     * @param scrollToBottom scroll to bottom flag
     */
    public void updateOrAddMessage(int oldId, MESSAGE newMessage, boolean scrollToBottom) {
        int position = getMessagePositionById(oldId);
        if (position >= 0) {
            Wrapper<MESSAGE> element = new Wrapper<>(newMessage);
            mItems.set(position, element);
            notifyItemChanged(position);
        } else {
            addToStart(newMessage, scrollToBottom);
        }
    }

    /**
     * Delete gif_message.
     *
     * @param message gif_message to be deleted.
     */
    public void delete(MESSAGE message) {
        deleteById(message.getMsgId());
    }

    /**
     * Delete by datetime
     *
     * @param message
     */
    public void deleteByDatetime(MESSAGE message) {
        deleteByDatetime(message.getDatetime());
    }

    /**
     * Delete gif_message by identifier.
     *
     * @param datetime identifier of gif_message.
     */
    public void deleteByDatetime(long datetime) {
        int index = getMessagePositionByDatetime(datetime);
        if (index >= 0) {
            mItems.remove(index);
            notifyItemRemoved(index);
        }
    }

    /**
     * Delete gif_message by identifier.
     *
     * @param id identifier of gif_message.
     */
    public void deleteById(int id) {
        int index = getMessagePositionById(id);
        if (index >= 0) {
            mItems.remove(index);
            notifyItemRemoved(index);
        }
    }

    /**
     * Delete messages.
     *
     * @param messages messages list to be deleted.
     */
    public void delete(List<MESSAGE> messages) {
        for (MESSAGE message : messages) {
            int index = getMessagePositionById(message.getMsgId());
            if (index >= 0) {
                mItems.remove(index);
                notifyItemRemoved(index);
            }
        }
    }

    /**
     * Delete messages by identifiers.
     *
     * @param ids ids array of identifiers of messages to be deleted.
     */
    public void deleteByIds(int[] ids) {
        for (int id : ids) {
            int index = getMessagePositionById(id);
            if (index >= 0) {
                mItems.remove(index);
                notifyItemRemoved(index);
            }
        }
    }

    /**
     * Clear messages list.
     */
    public void clear() {
        mItems.clear();
        notifyDataSetChanged();
    }

    /**
     * Enable selection mode.
     *
     * @param listener SelectionListener. To get selected messages use {@link #getSelectedMessages()}.
     */
    public void enableSelectionMode(SelectionListener listener) {
        if (listener == null) {
            throw new IllegalArgumentException("SelectionListener must not be null.");
        } else {
            mSelectionListener = listener;
        }
    }

    /**
     * Disable selection mode, and deselect all items.
     */
    public void disableSelectionMode() {
        mSelectionListener = null;
        deselectAllItems();
    }

    /**
     * Get selected messages.
     *
     * @return ArrayList with selected messages.
     */
    @SuppressWarnings("unchecked")
    public ArrayList<MESSAGE> getSelectedMessages() {
        ArrayList<MESSAGE> list = new ArrayList<>();
        for (Wrapper wrapper : mItems) {
            if (wrapper.item instanceof IMessage && wrapper.isSelected) {
                list.add((MESSAGE) wrapper.item);
            }
        }
        return list;
    }

    /**
     * Delete all selected messages
     */
    public void deleteSelectedMessages() {
        List<MESSAGE> selectedMessages = getSelectedMessages();
        delete(selectedMessages);
        deselectAllItems();
    }

    /**
     * Deselect all items.
     */
    public void deselectAllItems() {
        for (int i = 0; i < mItems.size(); i++) {
            Wrapper wrapper = mItems.get(i);
            if (wrapper.isSelected) {
                wrapper.isSelected = false;
                notifyItemChanged(i);
            }
        }
        mIsSelectedMode = false;
        mSelectedItemCount = 0;
        notifySelectionChanged();
    }

    private void notifySelectionChanged() {
        if (mSelectionListener != null) {
            mSelectionListener.onSelectionChanged(mSelectedItemCount);
        }
    }

    /**
     * Set onMsgClickListener, fires onClick event only if list is not in selection mode.
     *
     * @param listener OnMsgClickListener
     */
    public void setOnMsgClickListener(OnMsgClickListener<MESSAGE> listener) {
        mMsgClickListener = listener;
    }

    private View.OnClickListener getMsgClickListener(final Wrapper<MESSAGE> wrapper) {
        return new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mSelectionListener != null && mIsSelectedMode) {
                    wrapper.isSelected = !wrapper.isSelected;
                    if (wrapper.isSelected) {
                        incrementSelectedItemsCount();
                    } else {
                        decrementSelectedItemsCount();
                    }

                    MESSAGE message = (wrapper.item);
                    notifyItemChanged(getMessagePositionById(message.getMsgId()));
                } else {
                    notifyMessageClicked(view, wrapper.item);
                }
            }
        };
    }

    private void incrementSelectedItemsCount() {
        mSelectedItemCount++;
        notifySelectionChanged();
    }

    private void decrementSelectedItemsCount() {
        mSelectedItemCount--;
        mIsSelectedMode = mSelectedItemCount > 0;
        notifySelectionChanged();
    }

    private void notifyMessageClicked(View view, MESSAGE message) {
        if (mMsgClickListener != null) {
            mMsgClickListener.onMessageClick(view, message);
        }
    }

    /**
     * Set long click listener for item, fires only if selection mode is disabled.
     *
     * @param listener onMsgLongClickListener
     */
    public void setMsgLongClickListener(OnMsgLongClickListener<MESSAGE> listener) {
        mMsgLongClickListener = listener;
    }

    private View.OnLongClickListener getMessageLongClickListener(final Wrapper<MESSAGE> wrapper) {
        return new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view) {
                if (mSelectionListener == null) {
                    notifyMessageLongClicked(view, wrapper.item);
                    return true;
                } else {
                    mIsSelectedMode = true;
                    view.callOnClick();
                    return true;
                }
            }
        };
    }

    private void notifyMessageLongClicked(View view, MESSAGE message) {
        if (mMsgLongClickListener != null) {
            mMsgLongClickListener.onMessageLongClick(view, message);
        }
    }

    public void setOnAvatarClickListener(OnAvatarClickListener<MESSAGE> listener) {
        mAvatarClickListener = listener;
    }

    public void setLayoutManager(LinearLayoutManager layoutManager) {
        mLayoutManager = layoutManager;
    }

    public RecyclerView.LayoutManager getLayoutManager() {
        return mLayoutManager;
    }

    public void setStyle(Context context, MessageListStyle style) {
        mContext = context;
        mStyle = style;
    }

    public interface DefaultMessageViewHolder {
        void applyStyle(MessageListStyle style);
    }

    @Deprecated
    public interface OnLoadMoreListener {
        void onLoadMore(int page, int totalCount);
    }

    public interface SelectionListener {
        void onSelectionChanged(int count);
    }

    /**
     * Callback will invoked when gif_message item is clicked
     *
     * @param <MESSAGE>
     */
    public interface OnMsgClickListener<MESSAGE extends IMessage> {
        void onMessageClick(View view, MESSAGE message);
    }

    /**
     * Callback will invoked when gif_message item is long clicked
     *
     * @param <MESSAGE>
     */
    public interface OnMsgLongClickListener<MESSAGE extends IMessage> {
        void onMessageLongClick(View view, MESSAGE message);
    }

    public interface OnAvatarClickListener<MESSAGE extends IMessage> {
        void onAvatarClick(View view, MESSAGE message);
    }

    public void setMsgStatusViewClickListener(OnMsgStatusViewClickListener<MESSAGE> listener) {
        this.mMsgStatusViewClickListener = listener;
    }

    public interface OnMsgStatusViewClickListener<MESSAGE extends IMessage> {
        void onStatusViewClick(MESSAGE message);
    }

    public static class MyMultiItemEntity implements MultiItemEntity {

        // Text message
        public static final int TYPE_RECEIVE_TXT = 0;
        public static final int TYPE_SEND_TXT = 1;

        // Photo message
        public static final int TYPE_SEND_IMAGE = 2;
        public static final int TYPE_RECEIVER_IMAGE = 3;

        // Location message
        public static final int TYPE_SEND_LOCATION = 4;
        public static final int TYPE_RECEIVER_LOCATION = 5;

        // Voice message
        public static final int TYPE_SEND_VOICE = 6;
        public static final int TYPE_RECEIVER_VOICE = 7;

        // Video message
        public static final int TYPE_SEND_VIDEO = 8;
        public static final int TYPE_RECEIVE_VIDEO = 9;

        // Group change gif_message
        public static final int TYPE_EVENT = 10;

        //friend apply
        public static final int TYPE_FRIEND_APPLY = 15;

        private Class<? extends BaseMessageViewHolder<? extends IMessage>> mSendTxtHolder;
        private Class<? extends BaseMessageViewHolder<? extends IMessage>> mReceiveTxtHolder;

        private Class<? extends BaseMessageViewHolder<? extends IMessage>> mSendLocationHolder;
        private Class<? extends BaseMessageViewHolder<? extends IMessage>> mReceiveLocationHolder;

        private Class<? extends BaseMessageViewHolder<? extends IMessage>> mSendVoiceHolder;
        private Class<? extends BaseMessageViewHolder<? extends IMessage>> mReceiveVoiceHolder;

        private Class<? extends BaseMessageViewHolder<? extends IMessage>> mSendPhotoHolder;
        private Class<? extends BaseMessageViewHolder<? extends IMessage>> mReceivePhotoHolder;

        private Class<? extends BaseMessageViewHolder<? extends IMessage>> mSendVideoHolder;
        private Class<? extends BaseMessageViewHolder<? extends IMessage>> mReceiveVideoHolder;

        private Class<? extends BaseMessageViewHolder<? extends IMessage>> mEventMsgHolder;

        private Class<? extends BaseMessageViewHolder<? extends IMessage>> mFriendApplyHolder;

        private int mSendTxtLayout;
        private int mReceiveTxtLayout;

        private int mSendLocationLayout;
        private int mReceiveLocationLayout;

        private int mSendVoiceLayout;
        private int mReceiveVoiceLayout;

        private int mSendPhotoLayout;
        private int mReceivePhotoLayout;

        private int mSendVideoLayout;
        private int mReceiveVideoLayout;
        private int mEventLayout;
        private int mFriendApplyLayout;

        public MyMultiItemEntity() {
            mSendTxtHolder = DefaultTxtViewHolder.class;
            mReceiveTxtHolder = DefaultTxtViewHolder.class;

            mSendVoiceHolder = DefaultVoiceViewHolder.class;
            mReceiveVoiceHolder = DefaultVoiceViewHolder.class;

            mSendPhotoHolder = DefaultPhotoViewHolder.class;
            mReceivePhotoHolder = DefaultPhotoViewHolder.class;

            mSendVideoHolder = DefaultVideoViewHolder.class;
            mReceiveVideoHolder = DefaultVideoViewHolder.class;

            mEventMsgHolder = DefaultEventMsgViewHolder.class;
            mFriendApplyHolder = DefaultFriendViewHolder.class;

            mSendTxtLayout = R.layout.item_send_text;
            mReceiveTxtLayout = R.layout.item_receive_txt;

            mSendVoiceLayout = R.layout.item_send_voice;
            mReceiveVoiceLayout = R.layout.item_receive_voice;

            mSendPhotoLayout = R.layout.item_send_photo;
            mReceivePhotoLayout = R.layout.item_receive_photo;

            mSendVideoLayout = R.layout.item_send_video;
            mReceiveVideoLayout = R.layout.item_receive_video;

            mEventLayout = R.layout.item_event_message;

            mFriendApplyLayout = R.layout.item_friend_apply_message;
        }

        /**
         * In place of default send text gif_message style by passing custom view holder and layout.
         *
         * @param holder Custom view holder that extends BaseMessageViewHolder.
         * @param layout Custom send text gif_message layout.
         */
        public void setSenderTxtMsg(Class<? extends BaseMessageViewHolder<? extends IMessage>> holder,
                                    @LayoutRes int layout) {
            this.mSendTxtHolder = holder;
            this.mSendTxtLayout = layout;
        }

        /**
         * In place of default receive text gif_message style by passing custom view holder and layout.
         *
         * @param holder Custom view holder that extends BaseMessageViewHolder.
         * @param layout Custom receive text gif_message layout.
         */
        public void setReceiverTxtMsg(Class<? extends BaseMessageViewHolder<? extends IMessage>> holder,
                                      @LayoutRes int layout) {
            this.mReceiveTxtHolder = holder;
            this.mReceiveTxtLayout = layout;
        }

        /**
         * Customize send text gif_message layout.
         *
         * @param layout Custom send text gif_message layout.
         */
        public void setSenderLayout(@LayoutRes int layout) {
            this.mSendTxtLayout = layout;
        }

        /**
         * Customize receive text gif_message layout.
         *
         * @param layout Custom receive text gif_message layout.
         */
        public void setReceiverLayout(@LayoutRes int layout) {
            this.mReceiveTxtLayout = layout;
        }

        /**
         * In place of default send voice gif_message style by passing custom view holder and layout.
         *
         * @param holder Custom view holder that extends BaseMessageViewHolder.
         * @param layout Custom send voice gif_message layout.
         */
        public void setSenderVoiceMsg(Class<? extends BaseMessageViewHolder<? extends IMessage>> holder,
                                      @LayoutRes int layout) {
            this.mSendVoiceHolder = holder;
            this.mSendVoiceLayout = layout;
        }

        /**
         * Customize send voice gif_message layout.
         *
         * @param layout Custom send voice gif_message layout.
         */
        public void setSendVoiceLayout(@LayoutRes int layout) {
            this.mSendVoiceLayout = layout;
        }

        /**
         * In place of default receive voice gif_message style by passing custom view holder and layout.
         *
         * @param holder Custom view holder that extends BaseMessageViewHolder.
         * @param layout Custom receive voice gif_message layout.
         */
        public void setReceiverVoiceMsg(Class<? extends BaseMessageViewHolder<? extends IMessage>> holder,
                                        @LayoutRes int layout) {
            this.mReceiveVoiceHolder = holder;
            this.mReceiveVoiceLayout = layout;
        }

        /**
         * Customize receive voice gif_message layout.
         *
         * @param layout Custom receive voice gif_message layout.
         */
        public void setReceiveVoiceLayout(@LayoutRes int layout) {
            this.mReceiveVoiceLayout = layout;
        }

        /**
         * In place of default send photo gif_message style by passing custom view holder and layout.
         *
         * @param holder Custom view holder that extends BaseMessageViewHolder.
         * @param layout Custom send photo gif_message layout
         */
        public void setSendPhotoMsg(Class<? extends BaseMessageViewHolder<? extends IMessage>> holder,
                                    @LayoutRes int layout) {
            this.mSendPhotoHolder = holder;
            this.mSendPhotoLayout = layout;
        }

        /**
         * Customize send voice gif_message layout.
         *
         * @param layout Custom send photo gif_message layout.
         */
        public void setSendPhotoLayout(@LayoutRes int layout) {
            this.mSendPhotoLayout = layout;
        }

        /**
         * In place of default receive photo gif_message style by passing custom view holder and layout.
         *
         * @param holder Custom view holder that extends BaseMessageViewHolder.
         * @param layout Custom receive photo gif_message layout
         */
        public void setReceivePhotoMsg(Class<? extends BaseMessageViewHolder<? extends IMessage>> holder,
                                       @LayoutRes int layout) {
            this.mReceivePhotoHolder = holder;
            this.mReceivePhotoLayout = layout;
        }

        /**
         * Customize receive voice gif_message layout.
         *
         * @param layout Custom receive photo gif_message layout.
         */
        public void setReceivePhotoLayout(@LayoutRes int layout) {
            this.mReceivePhotoLayout = layout;
        }

        /**
         * In place of default send video gif_message style by passing custom view holder and layout.
         *
         * @param holder Custom view holder that extends BaseMessageViewHolder.
         * @param layout custom send video gif_message layout
         */
        public void setSendVideoMsg(Class<? extends BaseMessageViewHolder<? extends IMessage>> holder,
                                    @LayoutRes int layout) {
            this.mSendVideoHolder = holder;
            this.mSendVideoLayout = layout;
        }

        /**
         * Customize send voice gif_message layout.
         *
         * @param layout Custom send Video gif_message layout.
         */
        public void setSendVideoLayout(@LayoutRes int layout) {
            this.mSendVideoLayout = layout;
        }

        /**
         * In place of default receive video gif_message style by passing custom view holder and layout.
         *
         * @param holder Custom view holder that extends BaseMessageViewHolder.
         * @param layout Custom receive video gif_message layout
         */
        public void setReceiveVideoMsg(Class<? extends BaseMessageViewHolder<? extends IMessage>> holder,
                                       @LayoutRes int layout) {
            this.mReceiveVideoHolder = holder;
            this.mReceiveVideoLayout = layout;
        }

        /**
         * Customize receive video gif_message layout.
         *
         * @param layout Custom receive video gif_message layout.
         */
        public void setReceiveVideoLayout(@LayoutRes int layout) {
            this.mReceiveVideoLayout = layout;
        }

        @Deprecated
        public void setSendCustomMsg(Class<? extends BaseMessageViewHolder<? extends IMessage>> holder,
                                     @LayoutRes int layout) {
//            this.mCustomSendMsgHolder = holder;
//            this.mCustomSendMsgLayout = layout;
        }

        @Deprecated
        public void setReceiveCustomMsg(Class<? extends BaseMessageViewHolder<? extends IMessage>> holder,
                                        @LayoutRes int layout) {
//            this.mCustomReceiveMsgHolder = holder;
//            this.mCustomReceiveMsgLayout = layout;
        }

        public void setSendLocationMsg(Class<? extends BaseMessageViewHolder<? extends IMessage>> holder,
                                       @LayoutRes int layout) {
            this.mSendLocationHolder = holder;
            this.mSendLocationLayout = layout;
        }

        public void setReceiveLocationMsg(Class<? extends BaseMessageViewHolder<? extends IMessage>> holder,
                                          @LayoutRes int layout) {
            this.mReceiveLocationHolder = holder;
            this.mReceiveLocationLayout = layout;
        }


        public void setEventMessage(Class<? extends BaseMessageViewHolder<? extends IMessage>> holder,
                                    @LayoutRes int layout) {
            this.mEventMsgHolder = holder;
            this.mEventLayout = layout;
        }

        @Override
        public int getItemType() {
            return 0;
        }

    }

    private static class DefaultTxtViewHolder extends TxtViewHolder<IMessage> {

        public DefaultTxtViewHolder(View itemView, boolean isSender) {
            super(itemView, isSender);

        }
    }

    private static class DefaultVoiceViewHolder extends VoiceViewHolder<IMessage> {

        public DefaultVoiceViewHolder(View itemView, boolean isSender) {
            super(itemView, isSender);
        }
    }

    private static class DefaultPhotoViewHolder extends PhotoViewHolder<IMessage> {

        public DefaultPhotoViewHolder(View itemView, boolean isSender) {
            super(itemView, isSender);
        }
    }

    private static class DefaultVideoViewHolder extends VideoViewHolder<IMessage> {

        public DefaultVideoViewHolder(View itemView, boolean isSender) {
            super(itemView, isSender);
        }
    }

    private static class DefaultEventMsgViewHolder extends EventViewHolder<IMessage> {
        public DefaultEventMsgViewHolder(View itemView, boolean isSender) {
            super(itemView, isSender);
        }
    }

    private static class DefaultFriendViewHolder extends FriendApplyHolder<IMessage> {
        public DefaultFriendViewHolder(View itemView, boolean isSender) {
            super(itemView, isSender);
        }
    }

    @Override
    public void onViewDetachedFromWindow(@NonNull RecyclerView.ViewHolder holder) {
        super.onViewDetachedFromWindow(holder);
        ViewHolderController.getInstance().remove(holder.getAdapterPosition());
    }

    @Override
    public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
        super.onDetachedFromRecyclerView(recyclerView);
        ViewHolderController.getInstance().release();
    }

    public void clearListeners() {
        mMsgClickListener = null;
        mMsgLongClickListener = null;
        mAvatarClickListener = null;
        mMsgStatusViewClickListener = null;
        ViewHolderController.getInstance().release();
        ViewHolderController.getInstance().setReplayVoiceListener(null);
    }

}
